import json
from datetime import datetime, timedelta
from typing import Any, Optional

import pandas as pd
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.requests import Request
from starlette.responses import JSONResponse, Response

from src.infrastructure.db.models import Order, Proxy, Server, Statistics, User


async def get_chart_data(
    session: AsyncSession, date_from: Optional[datetime] = None, period: int = 31
) -> tuple[list[str], dict[str, list | list[int | Any] | str | Any], Any, Any]:
    if not date_from:
        date_from = datetime.now().date() - timedelta(days=period)

    dates = []
    registered_users = []
    orders_created = []
    servers_added = []
    proxies_created = []

    mobile_proxies_bought = []
    server_proxies_bought = []
    messages_sent = []
    payments_payed = []

    for i in range(0, period + 1):
        current_date = date_from + timedelta(days=i)
        dates.append(current_date.strftime("%Y-%m-%d"))

        res = await session.execute(select(func.count(User.id)).where(func.date(User.was_registered) == current_date))
        registered_users.append(res.scalar_one())

        res = await session.execute(select(func.count(Server.id)).where(func.date(Server.was_added) == current_date))
        servers_added.append(res.scalar_one())

        res = await session.execute(select(func.count(Order.id)).where(func.date(Order.was_created) == current_date))
        orders_created.append(res.scalar_one())

        res = await session.execute(select(func.count(Proxy.id)).where(func.date(Proxy.was_added) == current_date))
        proxies_created.append(res.scalar_one())

        res = await session.execute(
            select(
                Statistics.messages_sent,
                Statistics.mobile_proxies_bought,
                Statistics.server_proxies_bought,
                Statistics.payments_payed,
            ).where(func.date(Statistics.day) == current_date)
        )
        statistics = res.first()

        messages_sent.append(statistics.messages_sent if statistics else 0)
        mobile_proxies_bought.append(statistics.mobile_proxies_bought if statistics else 0)
        server_proxies_bought.append(statistics.server_proxies_bought if statistics else 0)
        payments_payed.append(statistics.payments_payed if statistics else 0)

    data = {
        "registered_users": registered_users,
        "proxies_created": proxies_created,
        "orders_created": orders_created,
        "servers_added": servers_added,
        "mobile_proxies_bought": mobile_proxies_bought,
        "server_proxies_bought": server_proxies_bought,
        "payments_payed": payments_payed,
        "messages_sent": messages_sent,
    }

    df = pd.DataFrame(data, index=dates)
    pd.options.display.float_format = "{:,.0f}".format
    df = df.loc[:, df.columns != "labels"]
    df.loc[len(df)] = df.sum(axis=0)
    if df.isnull().values.any():
        df.fillna(0, inplace=True)
    table_rows = df[0:-1].astype(int).values.tolist()
    rows_sums = df.iloc[-1].astype(int).values.tolist()

    data.update(
        {
            "dates": json.dumps(dates),
            "table_rows": table_rows,
            "rows_sums": rows_sums,
        }
    )

    return dates, data, table_rows, rows_sums


async def get_stats(request: Request):
    try:
        session: AsyncSession = request.state.session
        form = await request.form()
        date_from = datetime.strptime(form.get("date_from"), "%Y-%m-%d")
        date_to = datetime.strptime(form.get("date_to"), "%Y-%m-%d")
        period = abs(date_to - date_from).days
        dates, data, table_rows, rows_sums = await get_chart_data(session, date_from, period)
        return JSONResponse(
            {
                "data": data,
                "dates": dates,
                "table_rows": table_rows,
                "rows_sums": rows_sums,
            }
        )
    except ValueError:
        return Response(status_code=204)
